/**
 * @file uart.c
 * @brief T1 UART Controller Driver
 *
 * @author yemt@witsi.cn
 * @date 2011-08-10
 * @version T1.UART.01
 */
#include <string.h>
#include "regs.h"
#include "uart.h"
#include "dev.h"
#include "ictl.h"
#include "iodev.h"
#include "circbuf.h"
#include "config.h"
#include "ioctl.h"
#include "syscfg.h"

/************************************************************************
 * RX Buffer Synchronization
 * When the buffer is read by the outside, it needs to disable the
 * interrupt to prevent the buffer from wrote by the RX interrupt
 ************************************************************************/

#define disable_rx_it(reg)      (reg)->IER &= (~(UART_INT_RX_AVL))
#define enable_rx_it(reg)       (reg)->IER |= UART_INT_RX_AVL

#define enter_rx_critical(reg) disable_rx_it(reg)
#define exit_rx_critical(reg)  enable_rx_it(reg)

/************************************************************************
 * IO Device Interface
 ************************************************************************/

/**
 * The UART parameters structure
 */
typedef struct {
        serial_info_t * config; /**< Pointer to the UART Configuration */
        uint32_t base;          /**< UART Register Address */
        circbuf_t* circ_rx;     /**< Buffer storing the received bytes */
        uint32_t irq_no;        /**< UART Interrupt Number */
        irq_handler_t handler;  /**< UART Interrupt Handler */
}serial_param_t;

#ifdef CONFIG_POWER_CTL
volatile static uint8_t uart_sleep_flag = 0;
volatile static uint8_t uart1_flow_flag = 0;


static inline int is_uart_sleep_mode(void)
{
    return uart_sleep_flag;
}

static inline void uart_enter_sleep_mode(void)
{
    uart_sleep_flag = 1;
}

static inline void uart_exit_sleep_mode(void)
{
    uart_sleep_flag = 0;
}

static int is_data_recv(void)
{
#ifdef CONFIG_T1_UART1
    if (!gpio_input(72)) return 1;
#endif
#ifdef CONFIG_T1_UART2
    if (!gpio_input(66)) return 1;
#endif

#ifdef CONFIG_T1_UART3
    if (gpio_input(70)) return 1;
#endif
    return 0;
}
#endif
/**
 * @fn uart_config_port
 * @brief Configure the UART with a new configuration
 *
 * @param param         Point to the UART parameter structure
 * @param new_config    Point to the new configuration to be setup
 * @return void
 */
static void uart_config_port(serial_param_t *param, serial_info_t *new_config)
{
        uint32_t ccr;
        uart_reg_t * reg = (uart_reg_t *)(param->base);

        reg->GCR  = 0; /* disable uart */
        /* word length */
        ccr = UART_CHAR(new_config->word_length - SERIAL_WORD_LENGTH_5);

        if ((new_config->word_length == SERIAL_WORD_LENGTH_5) &&
                new_config->stop == SERIAL_STOP_2) {
                        new_config->stop = SERIAL_STOP_1_5;
        }

        if (new_config->stop != SERIAL_STOP_1) ccr |= UART_STOP_BIT_2;
        if (new_config->parity != SERIAL_PARITY_NONE) {
                ccr |= UART_PARITY_EN;
                if (new_config->parity == SERIAL_PARITY_EVEN)
                        ccr |= UART_PARITY_EVEN;
        }

        reg->CCR = ccr;
        reg->BRR = (CONFIG_PCLK0 >> 4) / new_config->baud;
        reg->ICR = 0xFFFFFFFF; /* clear all pendding flags */
#ifdef CONFIG_UART_FIFO
        reg->IER = UART_INT_RX_AVL | UART_INT_TIMEOUT; /* enable RX/TX interrupt */
        /* UART Enable, TX Enable, RX Enable, RX FIFO = 1, TX FIFO = 16 */
        reg->GCR = UART_TX_EN | UART_RX_EN | UART_EN | UART_RX_TRIG_N(2);
 #else        
         reg->IER = UART_INT_RX_AVL; /* enable RX/TX interrupt */
         /* UART Enable, TX Enable, RX Enable, RX FIFO = 1, TX FIFO = 16 */
         reg->GCR = UART_TX_EN | UART_RX_EN | UART_EN;
 #endif 

        if (param->base == UART1) { 
            if(new_config->flow_ctrl & (SERIAL_FLOW_RTSCTS_RX | SERIAL_FLOW_RTSCTS_TX))
            {
                reg->GCR |= UART_AF_EN;
#ifdef CONFIG_POWER_CTL                
                uart1_flow_flag = 1;
#endif
            }
            else {
                reg->GCR &= ~UART_AF_EN;
#ifdef CONFIG_POWER_CTL                         
                uart1_flow_flag = 0;
#endif
            }
        }
    
        if (new_config != param->config)
                *(param->config) = *new_config;
 
}
        
/**
 * @fn uart_interrupt
 * @brief UART Interrupt Handler
 *
 * @param param         Point to the UART parameter structure
 * @return void
 */
static void uart_interrupt(const serial_param_t * param)
{
        uart_reg_t * reg = (uart_reg_t *)(param->base);
        uint32_t isr = reg->ISR;
        reg->ICR = isr; /* Also clear other errors */
#ifdef CONFIG_UART_FIFO
        if (isr & (UART_INT_RX_AVL | UART_INT_TIMEOUT)) {
#else        
        if (isr & UART_INT_RX_AVL) {
#endif
                while (reg->CSR & UART_CSR_RX_AVL) {
                        CIRC_PUT(param->circ_rx, reg->RDR);
                        CIRC_PUT_TIDY(param->circ_rx);
                }
        }
}

/**
 * @fn t1_uart_init
 * @brief UART Initializer
 *
 * @param dev           Point to the UART IO device entry
 * @return
 *      -0  Success
 */
static int t1_uart_init(iodev_t *dev)
{
        serial_param_t * param =
                (serial_param_t *)(dev->priv);

        uart_config_port(param, param->config);
        request_irq(param->irq_no, param->handler);
        return 0;
}

/**
 * @fn t1_uart_write
 * @brief UART Writer
 *
 * @param dev           Point to the UART IO device entry
 * @param buffer        Store the data to be wrote
 * @param size          The size of the data to be wrote
 * @return
 *      ssize_t         The size wrote
 */
static ssize_t t1_uart_write(iodev_t *dev, const void* buffer, size_t size)
{
        serial_param_t * priv = (serial_param_t *)dev->priv;
        uart_reg_t * reg = (uart_reg_t *)(priv->base);
        uint8_t * pbuf = (uint8_t *)buffer;
        int n = size;

        while(n --) {
                while (reg->CSR & UART_CSR_TX_FULL);
                reg->TDR = *pbuf ++;
        }
        return size;
}

/**
 * @fn t1_uart_write
 * @brief UART Writer
 *
 * @param dev           Point to the UART IO device entry
 * @param buffer        Store the data read
 * @param size          The size of the data wanted
 * @return
 *      ssize_t         The actual size read
 */
static ssize_t t1_uart_read(iodev_t *dev, void *buffer, size_t size)
{
        serial_param_t * priv = (serial_param_t *)dev->priv;
        uart_reg_t * reg = (uart_reg_t *)(priv->base);
        int count = CIRC_CNT(priv->circ_rx);
        if (count == 0) return 0;
        if (size > count) size = count;

        enter_rx_critical(reg);
        circ_buf_get(priv->circ_rx, buffer, size);
        exit_rx_critical(reg);

        return size;
}



	

/**
 * @fn t1_uart_ioctl
 * @brief UART Controller
 *
 * @param dev           Point to the UART IO device entry
 * @param cmd           Controlling command
 * @param arg           Point to the parameters for the command
 * @return
 *      <0              Command failed
 *      =0              Success
 */
static int t1_uart_ioctl(iodev_t *dev, uint32_t cmd, void *arg)
{
        switch (cmd) {
        case IOCTL_SERIAL_SET_PARAM:
                {
                        serial_info_t * info = (serial_info_t *) arg;
                        if (info) {
                                uart_config_port((serial_param_t *)(dev->priv),
                                                info);
                        }
                }
                break;
        case IOCTL_SERIAL_GET_PARAM:
                if (arg) {
                        memcpy(arg, (uint8_t *)(dev->priv),
                                        sizeof (serial_info_t));
                }
                break;
        case IOCTL_SERIAL_CLR_RXBUF:
                {
                        serial_param_t * priv = (serial_param_t *)dev->priv;
                        CIRC_CLEAR(priv->circ_rx);
                }
                break;
        case IOCTL_SERIAL_CLR_TXBUF:
                return 0;
        default: 
		{
#ifdef CONFIG_SWITCH_UART
			extern int board_uart_ioctl(uint32_t cmd, void *arg);
			return board_uart_ioctl(cmd,arg);
#else
			return -1;
#endif
       		 }
        }
        return 0;
}

/**
 * @fn t1_uart_poll
 * @brief UART Poller
 *
 * @param dev           Point to the UART IO device entry
 * @param flags         The Status to be polled
 * @return
 *      int             The device status
 */
static int t1_uart_poll(iodev_t *dev, uint32_t flags)
{
        serial_param_t * priv = (serial_param_t *)dev->priv;
        if (flags & POLL_READ) {
 #ifdef CONFIG_POWER_CTL
                if (is_uart_sleep_mode()) {
                    if (!is_data_recv()) {
                        flags &= ~POLL_READ;
                    }
                }
                else 
#endif
                if (CIRC_EMPTY(priv->circ_rx))
                        flags &= ~POLL_READ;
        }

        return flags;
}

#ifdef CONFIG_POWER_CTL
static int t1_uart_suspend(iodev_t *dev,int sleep)
{

    if (sleep == 3) {
        uart_enter_sleep_mode();
#ifdef CONFIG_T1_UART1
        SYSCFG->PIN_MUXING1 |= GPIO71_72_DIS;
        gpio_set_input(72, 0);
        if (uart1_flow_flag) {
            SYSCFG->PIN_MUXING1 &= ~GPIO69_70_FUNC_MSK;
            SYSCFG->PIN_MUXING1 |= GPIO69_70_EN;
            gpio_set_output(69, 0);
            gpio_output(69, 1);
            gpio_set_input(70, 0);
        }
#endif
#ifdef CONFIG_T1_UART2
        SYSCFG->PIN_MUXING1 &= ~GPIO65_66_FUNC_MSK;
        SYSCFG->PIN_MUXING1 |= GPIO65_66_EN;
        gpio_set_input(66, 0);
#endif

#ifdef CONFIG_T1_UART3
        SYSCFG->PIN_MUXING1 &= ~GPIO69_70_FUNC_MSK;
        SYSCFG->PIN_MUXING1 |= GPIO69_70_EN;
        gpio_set_input(70, 0);
#endif
    }
    return 0;
}


static int t1_uart_wakeup(iodev_t *dev, int sleep)
{
    if (sleep == 3) {
#ifdef CONFIG_T1_UART1
        SYSCFG->PIN_MUXING1 &= ~GPIO71_72_DIS;
        mdelay(1);
        uint8_t data;
        t1_uart_read(dev, &data, 1);
        if (uart1_flow_flag) {
            SYSCFG->PIN_MUXING1 &= ~GPIO69_70_FUNC_MSK;
            SYSCFG->PIN_MUXING1 |= nRTS1_nCTS1_PIN_EN;
        }
#endif
#ifdef CONFIG_T1_UART2
        SYSCFG->PIN_MUXING1 &= ~GPIO65_66_FUNC_MSK;
        SYSCFG->PIN_MUXING1 |= TXD2_RXD2_PIN_EN;
#endif
#ifdef CONFIG_T1_UART3
        SYSCFG->PIN_MUXING1 &= ~GPIO69_70_FUNC_MSK;
        SYSCFG->PIN_MUXING1 |= TXD3_RXD3_PIN_EN;
#endif
        uart_exit_sleep_mode();
    }
    return 0;
}
#endif

#ifdef CONFIG_T1_UART1

/**
 * UART1 Receiver Buffer
 */
DECLARE_STATIC_CIRC_BUF(uart1_rx_buff, CONFIG_UART1_RX_BUFF_LEN);

/**
 * UART1 Configuration (default):
 * 115200,8,n 1
 */
static serial_info_t uart1_config = {
        .baud = CONFIG_UART1_BAUD,
        .stop = SERIAL_STOP_1,
        .parity = SERIAL_PARITY_NONE,
        .word_length = SERIAL_WORD_LENGTH_8,
        .flow_ctrl = SERIAL_FLOW_NONE,
};

static void uart1_irq_handler(void);

/**
 * UART1 Parameters Structure
 */
static const serial_param_t uart1 = {
        .config = &uart1_config,
        .base = (uint32_t)T1_UART1_BASE,
        .circ_rx = &uart1_rx_buff,
        .irq_no = IRQ_UART1,
        .handler = uart1_irq_handler,
};

/**
 * UART1 IO Device Entry
 */
static const iodev_t t1_serial1 __iodev_entry = {
        .name = "ser1",
        .priv = &uart1,
        .init = t1_uart_init,
        .read = t1_uart_read,
        .write = t1_uart_write,
        .ioctl = t1_uart_ioctl,
        .poll = t1_uart_poll,
#ifdef CONFIG_POWER_CTL
        .suspend = t1_uart_suspend,
        .wakeup = t1_uart_wakeup,
#endif
};

static void uart1_irq_handler(void)
{
        uart_interrupt(&uart1);
}

#endif


#ifdef CONFIG_T1_UART2

/**
 * UART1 Receiver Buffer
 */
DECLARE_STATIC_CIRC_BUF(uart2_rx_buff, CONFIG_UART2_RX_BUFF_LEN);

/**
 * UART1 Configuration (default):
 * 115200,8,n 1
 */
static serial_info_t uart2_config = {
        .baud = CONFIG_UART2_BAUD,
        .stop = SERIAL_STOP_1,
        .parity = SERIAL_PARITY_NONE,
        .word_length = SERIAL_WORD_LENGTH_8,
        .flow_ctrl = SERIAL_FLOW_NONE,
};

static void uart2_irq_handler(void);

/**
 * UART1 Parameters Structure
 */
static const serial_param_t uart2 = {
        .config = &uart2_config,
        .base = (uint32_t)T1_UART2_BASE,
        .circ_rx = &uart2_rx_buff,
        .irq_no = IRQ_UART2,
        .handler = uart2_irq_handler,
};

/**
 * UART1 IO Device Entry
 */
static const iodev_t t1_serial2 __iodev_entry = {
        .name = "ser2",
        .priv = &uart2,
        .init = t1_uart_init,
        .read = t1_uart_read,
        .write = t1_uart_write,
        .ioctl = t1_uart_ioctl,
        .poll = t1_uart_poll,
#ifdef CONFIG_POWER_CTL
        .suspend = t1_uart_suspend,
        .wakeup = t1_uart_wakeup,
#endif
};

static void uart2_irq_handler(void)
{
        uart_interrupt(&uart2);
}

#endif

#ifdef CONFIG_T1_UART3

/**
 * UART1 Receiver Buffer
 */
DECLARE_STATIC_CIRC_BUF(uart3_rx_buff, CONFIG_UART3_RX_BUFF_LEN);

/**
 * UART1 Configuration (default):
 * 115200,8,n 1
 */
static serial_info_t uart3_config = {
        .baud = CONFIG_UART3_BAUD,
        .stop = SERIAL_STOP_1,
        .parity = SERIAL_PARITY_NONE,
        .word_length = SERIAL_WORD_LENGTH_8,
        .flow_ctrl = SERIAL_FLOW_NONE,
};

static void uart3_irq_handler(void);

/**
 * UART1 Parameters Structure
 */
static const serial_param_t uart3 = {
        .config = &uart3_config,
        .base = (uint32_t)T1_UART3_BASE,
        .circ_rx = &uart3_rx_buff,
        .irq_no = IRQ_UART3,
        .handler = uart3_irq_handler,
};

/**
 * UART1 IO Device Entry
 */
static const iodev_t t1_serial3 __iodev_entry = {
        .name = "ser3",
        .priv = &uart3,
        .init = t1_uart_init,
        .read = t1_uart_read,
        .write = t1_uart_write,
        .ioctl = t1_uart_ioctl,
        .poll = t1_uart_poll,
#ifdef CONFIG_POWER_CTL
        .suspend = t1_uart_suspend,
        .wakeup = t1_uart_wakeup,
#endif
};

static void uart3_irq_handler(void)
{
        uart_interrupt(&uart3);
}

#endif



